home *** CD-ROM | disk | FTP | other *** search
/ Nautilus 1992 July / Nautilus-3-8 / Nautilus-3-8.bin / Tools & Utilities / Techy Stuff / Source ƒ / Dragonsmith 1.0b2 / Dragon.c < prev    next >
Encoding:
Text File  |  1992-05-20  |  19.4 KB  |  586 lines

  1. /*
  2.     Dragon.c
  3.     
  4.     Simple framework for a drag-n-drop application.  A "dragon" does something meaningful (one
  5.     would hope) with the Finder objects that were drag-n-dropped onto it, then immediately quits.
  6.     
  7.     This class does nothing by itself; simple subclasses will need to override ProcessDroppings
  8.     ╤ more powerful subclasses may need to much more (e.g., override event handling methods).
  9.     
  10.     Developed using THINK C 5.0 and ResEdit 2.1.1
  11.     
  12.     Copyright ⌐ 1992 by Paul M. Hoffman
  13.     Send feedback to paul.hoffman@um.cc.umich.edu
  14.     
  15.     This code may be freely used, altered, and distributed in any way you want as long as:
  16.         1.    It is GIVEN away rather than sold;
  17.         2.    This statement and the above copyright notice are left intact.
  18.     
  19.     Created    09 Apr 1992    v0.1.1    First attempt.  Brings up the alert OK but crashes with a bus
  20.                                     error in the 'PACK' 8 code executed after return from
  21.                                     HandleOdoc
  22.     Modified    11 Apr 1992    v0.1.2    No improvement
  23.                         v0.1.3    First working code.  I think the problem was that HandleOdoc
  24.                                     was returning an invalid error ╤ it was calling _DisposPtr
  25.                                     on a free block, which produces error -111, which is not
  26.                                     a valid AE error ╤ but why the hell should this cause a
  27.                                     bus error???
  28.                                 Discovered that 'oapp' is sent ONLY if nothing was
  29.                                     drag-n-dropped; fixed HandleOapp accordingly (v0.1.3b
  30.                                     and v0.1.3c)
  31.             12 Apr 1992    v0.2.0    Extracted ProcessDroppings.  You can now make "plug-in
  32.                                     dragons" by following the instructions in the file
  33.                                     ProcessDroppings.h.  This setup should keep recompile
  34.                                     times low
  35.                                 Memory management could be improved by making a call
  36.                                     to TempNewHandle and HLock instead of NewPtr
  37.                                 Overall error handling needs to be worked on.  I haven't even
  38.                                     tested Error () and Abort () to make sure they work!
  39.                                 Implemented testing using TestDragon.c
  40.                         v0.2.1    Fixed totally stupid sizeof (FSSpec*) when what I needed was
  41.                                     sizeof (FSSpec)
  42.             14 Apr 1992    v0.2.2    We now pass an FSSpec ** to ProcessDroppings instead of an
  43.                                     FSSpec * ╤ and the block is not locked
  44.             24 Apr 1992            OOPSied ╤ we're dealing with objects now!
  45.                                 Incorporated TestDragon.c ╤ what the hell, let compile times
  46.                                     suffer all they want!
  47.             30 Apr 1992            Added autoQuit instance variable
  48.                                 Added stubs for menus
  49.                                 Added DoOapp and DoOdoc methods and gave them the code
  50.                                     that was in HandleOapp and HandleOdoc 
  51.             01 May 1992    v0.2.3    Completely rewrote Test method to use ResEdit files with 'alis'
  52.                                     resources (which can be created using DalisMaker) ╤
  53.                                     with the old Test, ProcessDroppings could never get an
  54.                                     FSSpec to a folder or volume
  55.             05 May 1992    v0.2.4    Added a check for 'FREF' resources in the .rsrc file, so Test
  56.                                     won't pass FSSpec's for any verboten types of Finder
  57.                                     objects.  For example, what if you're testing a dragon that
  58.                                     will, when finished, strip the resource fork from 'TEXT' files
  59.                                     drag-and-dropped on it ╤ and while testing you stupidly
  60.                                     let ProcessDroppings get a hold of an FSSpec to your
  61.                                     favorite golf game ╤ AAAAAIIIIGGGGHHHHHHH!!!!!!!!)
  62.                                 Moved functions from #ifdef DEBUG section to FileUtils.c
  63.             12 May 1992            Fixed stupidity with InitMenus ╤ renamed method to
  64.                                     SetUpMenus (duh!) and moved to proper place (ack!)
  65.             14 May 1992    v0.3.0    Added fuller event handling (mostly empty methods) and tidied
  66.                                     things up a bit in preparation for first release
  67.             17 May 1992    v0.3.1    When run in the THINK C Debugger, we get an 'oapp' event but
  68.                                     calling AEProcessAppleEvent doesn't work ╤ it returns
  69.                                     an error of -608 (noOutstandingHLE).  This means we have
  70.                                     to keep the call to Test in Run rather than moving it to
  71.                                     DoOapp, where it seems to belong!  Boy, was I confused!
  72.                                 OK, I should've realized DoOdoc has to call SetUpMenus if
  73.                                     menusInstalled == FALSE.  Duh!
  74.             17 May 1992    v1.0b1    First release
  75.             20 May 1992    v1.0b2    Second release ╤ more comments, improved File Paths dragon
  76. */
  77.  
  78. #include    "Dragon.h"
  79.  
  80. Dragon::Dragon (void)
  81. {
  82.     autoQuit = TRUE;            // A subclass's constructor (which will be called AFTER this one)
  83.                             //     should set autoQuit to FALSE instead if it hangs around after
  84.                             //     starting up ╤ see DoOapp for the reason why
  85.     menusInstalled = FALSE;    // No menus ╤ but maybe a subclass will override us (ooh!)
  86. }
  87.  
  88. void Dragon::Start (void)
  89. {
  90.     InitMac ();
  91.     InitMilieu ();
  92.     InitMem ();
  93. }
  94.  
  95. void Dragon::InitMac (void)
  96. {
  97.     MaxApplZone ();
  98.     InitGraf (&thePort);
  99.     InitFonts ();
  100.     InitWindows ();
  101.     InitMenus ();
  102.     TEInit ();
  103.     InitDialogs (NULL);
  104.     InitCursor ();
  105. }
  106.  
  107. void Dragon::InitMilieu (void)
  108. {
  109.     OSErr    err;
  110.     long        result;
  111.     Boolean    hasAppleEvents;
  112.     
  113.     err = Gestalt (gestaltAppleEventsAttr, &result);
  114.     hasAppleEvents = result & 1L;        // If bit 0 ( == gestaltAppleEventsPresent) is set, we have 'em!
  115.     if (hasAppleEvents)
  116.         InitAppleEvents ();
  117.     else
  118.         Abort (eNoAppleEvents);
  119. }
  120.  
  121. void Dragon::InitAppleEvents (void)
  122. {
  123.     OSErr    err;
  124.     
  125.     if ((err = AEInstallEventHandler (kCoreEventClass, kAEOpenApplication, HandleOapp, 0, FALSE))
  126.         || (err = AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, HandleOdoc, 0, FALSE))
  127.         || (err = AEInstallEventHandler (kCoreEventClass, kAEPrintDocuments, HandlePdoc, 0, FALSE))
  128.         || (err = AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, HandleQuit, 0, FALSE))
  129.     )
  130.         Abort (eCouldntInstallAppleEvents);
  131. }
  132.  
  133. void Dragon::InitMem (void)
  134. {    
  135.     CallMoreMasters ();
  136. }
  137.  
  138. void Dragon::CallMoreMasters (void)
  139. {
  140.     short        mmCalls, **mmCallsHandle;
  141.     
  142.     // Call MoreMasters the number of times specified in the 'MoMa' resource ╤ each call gives us
  143.     //     an extra 64 (?) master pointers
  144.     mmCallsHandle = (short **) GetResource ('MoMa', 128);
  145.     if (mmCallsHandle == NULL)        // If there is no 'MoMa' resource, call it a few times
  146.         mmCalls = 3;
  147.     else
  148.         mmCalls = **mmCallsHandle;
  149.     for ( ; mmCalls > 0; mmCalls--)
  150.         MoreMasters ();
  151. }
  152.  
  153. void Dragon::SetUpMenus (void)
  154. {
  155.     // Override this method if you actually WANT menus (imagine!)
  156.     // Make sure you set menusInstalled = TRUE after you install
  157.     //     your menus ╤ otherwise, DoMenu will never get called!
  158.     // It's also a good idea to abort your SetUpMenus method if
  159.     //    menusInstalled == TRUE ╤ just in case there's some
  160.     //    weirdness with Apple Events while debugging (Q: What
  161.     //    would happen if Test and DoOapp were both called, and
  162.     //    we didn't make this check? ╤ A: The subclass's
  163.     //    SetUpMenus would be called twice ╤ crashola!)
  164. }
  165.  
  166. void Dragon::Run (void)
  167. {
  168.     #define    kSleepTime        6        // Sleep time should be in the neighborhood of 2 < n < 16
  169.                                     // A better idea is to store the sleep time in a resource, but
  170.                                     //     that seems like overkill here.  (You could always
  171.                                     //     override this method if you wanted to do it that way.)
  172.     
  173.     Boolean        gotEvent;
  174.     EventRecord    event;
  175.     
  176.     running = TRUE;
  177.  
  178. #ifdef DEBUG
  179.     Test ();        // If we're debugging, we have to run through the test now ╤ we'll never get an 'oapp'
  180.                 //    or 'odoc' event
  181.                 // VERY IMPORTANT NOTE:
  182.                 //     Actually, we DO get 'oapp' but when we call AEProcessAppleEvent, we get an
  183.                 //    error of -608 == noOutstandingHLE (#!$@#?!).  I don't understand this at all, but
  184.                 //    calling Test here seems to work just fine, so ╔
  185. #endif
  186.     
  187.     while (running) {
  188.         gotEvent = WaitNextEvent (everyEvent, &event, kSleepTime, NULL);
  189.         if (gotEvent)
  190.             DoEvent (&event);
  191.         else
  192.             DoIdle ();
  193.     }
  194. }
  195.  
  196. OSErr Dragon::ProcessDroppings (FSSpec **docs, long numDocs)
  197. {
  198.     // Override this method, or your dragon won't do nothin'
  199.     
  200.     DisposHandle ((Handle) docs);
  201. }
  202.  
  203. #ifdef DEBUG            // This should be defined in the project prefix while testing and debugging
  204.  
  205.                     // You won't get any Apple Events while debugging in THINK C (unless you do some
  206.                     //     fancy stuff in another application) ╤ so the Run method calls Test before it
  207.                     //     enters the main loop.
  208.                     
  209.                     // NOTE:    An application launched under System 7 will get either an 'oapp' or 'odoc'
  210.                     //         event, not both!  Inside Macintosh doesn't say it, but it IMPLIES it ╤ and
  211.                     //         experience proves it
  212.                     
  213.                     // This means that Test must have the same functionality (with perhaps a little less
  214.                     //     error checking) as DoOdoc AND DoOapp.  Specifically ╤
  215.                     //     1.    Test must pass a Handle to some FSSpecs to ProcessDroppings and
  216.                     //         finish by testing autoQuit and acting accordingly.  It gets these FSSpecs
  217.                     //         by resolving aliases ('alis' resources) read from a file ╤ you can make the
  218.                     //         file with the "alisMaker" dragon (see the read-me file in its folder).
  219.                     //     2.    It also means that Test must NOT pass FSSpecs which CAN'T be
  220.                     //         drag-and-dropped on the finished dragon!  Therefore we check for 'FREF'
  221.                     //         resources in the project's .rsrc file ╤ that's essentially what the System
  222.                     //         does in the instant it takes it to decide whether to allow drag-and-drop.
  223.                     //         Therefore you should have at least one 'FREF' resource in your .rsrc file,
  224.                     //         or Test won't do nuttin', honey!  (Actually, you should have two ╤ the first
  225.                     //         one is for type 'APPL' and is needed to get the Finder to show your icon.)
  226.                     //    3.    It must call SetUpMenus if autoQuit == FALSE
  227.                     
  228.                     // Of course, what we REALLY need is a way to feed Apple Events to the project when
  229.                     //    it's running in the THINK C Debugger
  230.                     
  231. void Dragon::Test (void)
  232. {
  233.     FSSpec            **fssHndl, *fss;
  234.     StandardFileReply    reply;
  235.     OSType            resEdType = 'rsrc', **fileTypesHndl;
  236.     short            refNum, numAlises, i, numFSSpecs, numFileTypes;
  237.     Boolean            wasChanged;
  238.     OSErr            err;
  239.     AliasHandle        alisHndl;
  240.     
  241.     // Get a list of file types that this dragon can open from the 'FREF' resources in the project's
  242.     //     .rsrc file ╤ we call this first so Count1Resources and Get1Resource will work correctly
  243.     //     below.  This is because whereas NOW the most recently opened resource fork is from
  244.     //     the .rsrc file, this will NOT be the case a little later when we open the 'alis' resources file
  245.     fileTypesHndl = FREFTypes (&numFileTypes);
  246.     if (fileTypesHndl == NULL)
  247.         return;
  248.         
  249.     // Pick a ResEdit file to get the 'alis' resources from
  250.     StandardGetFile (NULL, 1, &resEdType, &reply);
  251.     if (reply.sfGood) {
  252.     
  253.         // Open the resource fork of the file chosen and find out how many 'alis' resources there are
  254.         refNum = FSpOpenResFile (&reply.sfFile, fsRdPerm);
  255.         err = ResError ();
  256.         if (err == noErr) {
  257.             numAlises = Count1Resources ('alis');
  258.             
  259.             // Allocate a block for the FSSpecs that we'll end up passing to ProcessDroppings
  260.             fssHndl = (FSSpec **) AnyHandle (numAlises * sizeof (FSSpec));
  261.             if (fssHndl != NULL) {
  262.                 MoveHHi ((Handle) fssHndl);        // Move the block up out of the way and
  263.                 HLock ((Handle) fssHndl);        //     lock it for fast dereferencing later
  264.                 
  265.                 // Go through the 'alis' resources and (attempt to) resolve each one to an FSSpec
  266.                 //     ╤ when the loop finishes, numFSSpecs will have been set to the number of things
  267.                 //     we're going to pass to ProcessDroppings
  268.                 for (i = 1, fss = *fssHndl, numFSSpecs = 0; i <= numAlises; i++) {
  269.                     alisHndl = (AliasHandle) Get1IndResource ('alis', i);
  270.                     if (alisHndl != NULL) {
  271.  
  272.                         // Convert the 'alis' resource to an FSSpec record (pointed to by fss)
  273.                         //     ╤ we ignore the value returned in wasChanged
  274.                         err = ResolveAlias (NULL, alisHndl, fss, &wasChanged);
  275.                         if (err != noErr)    {                // Skip to the next iteration of the for loop if
  276.                             continue;                    //     we couldn't resolve the 'alis' resource
  277.                         }
  278.  
  279.                         // Now check to make sure fss contains a digestible FSSpec
  280.                         if (FSpOpenableType (fss, numFileTypes, fileTypesHndl)) {
  281.                                 fss++;
  282.                                 numFSSpecs++;
  283.                         }
  284.                         DisposHandle ((AliasHandle) alisHndl);    // Don't forget to dispose of the block!
  285.                     }
  286.                 }
  287.                 HUnlock ((Handle) fssHndl);
  288.                 FSClose (refNum);
  289.                 if (numFSSpecs < numAlises)                // Waste not, want not╔
  290.                     SetHandleSize ((Handle) fssHndl, numFSSpecs * sizeof (FSSpec));
  291.                 (void) ProcessDroppings (fssHndl, numFSSpecs);
  292.             } else
  293.                 FSClose (refNum);
  294.         } // if (err == noErr)
  295.     } // if (reply.sfGood)
  296.     
  297.     if (autoQuit)            // Should we quit immediately?
  298.         StopRunning ();
  299.     else
  300.         SetUpMenus ();
  301. }
  302.  
  303. #endif DEBUG
  304.  
  305. void Dragon::DoEvent (EventRecord *event)
  306. {
  307.     switch (event->what) {
  308.         case mouseDown:
  309.             DoMouseDown (event);
  310.             break;
  311.         case mouseUp:
  312.             DoMouseUp (event);
  313.             break;
  314.         case keyDown:
  315.         case autoKey:
  316.             DoKeyDown (event);
  317.             break;
  318.         case activateEvt:
  319.             DoActivate (event);
  320.             break;
  321.         case updateEvt:
  322.             DoUpdateEvent (event);
  323.             break;
  324.         case diskEvt:
  325.             DoDiskInsert (event);
  326.         case osEvt:                // Suspend, resume, and mouse-moved events
  327.             DoOSEvent (event);
  328.             break;
  329.         case kHighLevelEvent:
  330.             DoHighLevelEvent (event);
  331.             break;
  332.     }
  333. }
  334.  
  335. void Dragon::DoMouseDown (EventRecord *theEvent)
  336. {
  337.     WindowPtr    whichWindow;
  338.     short        domain;
  339.     
  340.     domain = FindWindow (theEvent->where, &whichWindow);
  341.     switch (domain) {
  342.         case inSysWindow:
  343.             SystemClick (theEvent, whichWindow);
  344.             break;
  345.         case inMenuBar:
  346.             // Watch out for stray mouseDowns in non-existent menus ╔
  347.             if (menusInstalled)
  348.                 DoMenu (MenuSelect (theEvent->where));
  349.             break;
  350.         default:
  351.             break;
  352.     }
  353. }
  354.  
  355. void Dragon::DoMouseUp (EventRecord *theEvent)
  356. {
  357.     // Do nothing
  358. }
  359.  
  360. void Dragon::DoKeyDown (EventRecord *theEvent)
  361. {
  362.     long        ticks;
  363.     
  364.     if ((theEvent->modifiers & cmdKey) && menusInstalled)    // If we have menus, handle
  365.         DoMenu (MenuKey (theEvent->message));            //     cmd-key combos
  366. }
  367.  
  368. void Dragon::DoActivate (EventRecord *theEvent)
  369. {
  370.     // Null method ╤ override if you have windows
  371. }
  372.  
  373. void Dragon::DoUpdateEvent (EventRecord *theEvent)
  374. {
  375.     // Null method ╤ override if you have windows
  376. }
  377.  
  378. void Dragon::DoDiskInsert (EventRecord *theEvent)
  379. {
  380.     Point    pt = {100, 100};        // Would { -1, -1 } give us a centered dialog??
  381.     
  382.     if ((short) (theEvent->message >> 16) != noErr)        // If the disk isn't formatted (or has problems),
  383.         (void) DIBadMount (pt, theEvent->message);    //     give the user the opportunity to initialize it
  384. }
  385.  
  386. void Dragon::DoOSEvent (EventRecord *theEvent)
  387. {
  388.     switch ((theEvent->message >> 24) & 0x00FF) {        // High byte tells us what kind of event it is
  389.         case suspendResumeMessage:            
  390.             if (theEvent->message & resumeFlag)
  391.                 DoResume ();
  392.             else
  393.                 DoSuspend ();
  394.             break;
  395.         case mouseMovedMessage:
  396.             break;
  397.         default:
  398.             break;
  399.     }
  400. }
  401.  
  402. void Dragon::DoHighLevelEvent (EventRecord *theEvent)
  403. {
  404.     short    err;
  405.     
  406.     // We assume all high level events are Apple Events
  407.     err = AEProcessAppleEvent (theEvent);
  408. }
  409.  
  410. void Dragon::DoMenu (long menuItemCode)
  411. {
  412.     // Override this method if you need menus
  413.     // Make sure you set menusInstalled in
  414.     //     SetUpMenus, or you'll never get here!
  415. }
  416.  
  417. void Dragon::DoIdle (void)
  418. {
  419.     // Override this to do periodic actions
  420. }
  421.  
  422. void Dragon::DoSuspend (void)
  423. {
  424.     // Override this to do something special when the dragon is switched out
  425. }
  426.  
  427. void Dragon::DoResume (void)
  428. {
  429.     // Override this to do something special when the dragon is switched back in
  430. }
  431.  
  432. void Dragon::StopRunning (void)
  433. {
  434.     running = FALSE;
  435. }
  436.  
  437. void Dragon::Finish (void)
  438. {
  439.     /* What is there to do?
  440.         Release any temporary memory (I don't know if the system will do this for you)
  441.         Delete any temporary files
  442.         Remove any trap patches
  443.         etc. (look at CApplication.c in the THINK Class Library for some ideas)
  444.     */
  445. }
  446.  
  447. pascal OSErr HandleOapp (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
  448. {
  449.     // NOTE:    We don't get an 'oapp' event unless nothing was drag-n-dropped ╤ this is
  450.     //         never explicitly stated in Inside Macintosh vol. 6 but seems clearly to be true
  451.     //         (and it makes a lot of more sense than if BOTH events were sent)
  452.     // A better way to do things would be to stuff a reference to the dragon object in the event
  453.     //     handler's refCon.  This would essentially eliminate the need for the gDragon global ╔
  454.     
  455.     OSErr    err;
  456.     
  457.     err = gDragon->DoOapp (theAppleEvent, theReply, refCon);
  458.     return err;
  459. }
  460.  
  461. pascal OSErr HandleOdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
  462. {
  463.     OSErr    err;
  464.     
  465.     err = gDragon->DoOdoc (theAppleEvent, theReply, refCon);
  466.     return err;
  467. }
  468.  
  469. pascal OSErr HandlePdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
  470. {
  471.     return noErr;            // Ignore 'pdoc' events ╤ most subclasses won't need to override
  472. }
  473.  
  474. pascal OSErr HandleQuit (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
  475. {
  476.     gDragon->StopRunning ();    // This'll keep us from going through the main event loop again
  477.     return noErr;
  478. }
  479.  
  480. OSErr Dragon::DoOapp (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
  481. {
  482.     // Override this method if you want your dragon do something when it's double-clicked
  483.     
  484.     // The default SetUpMenus doesn't do anything ╤ this is just here so a subclass can override
  485.     //     SetUpMenus and call inherited::DoOapp in its DoOapp method.  It seemed to make sense
  486.     //     at the time ╔
  487.     
  488.     if (autoQuit)            // Should we quit immediately? ╤ autoQuit will normally be set in
  489.                         //     your dragon's constructor method (here, Dragon::Dragon)
  490.         StopRunning ();
  491.     else
  492.         SetUpMenus ();    // If we don't quit right after drag-and-dropping, then we'd better
  493.                         //     have some menus to let the poor user do whatever it is we
  494.                         //     expect them to do after we hang around (like quit, at least!)
  495. }
  496.  
  497. OSErr Dragon::DoOdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
  498. {
  499.     // You probably won't need to override this method ╤ it calls ProcessDroppings with information
  500.     //     gleaned from the Apple Event.  Then again, you could filter out folders here rather than in
  501.     //     ProcessDroppings
  502.     
  503.     register OSErr        err;
  504.     AEDescList        docList;
  505.     long                i, numDocs, actualSize;
  506.     FSSpec            *fss;
  507.     AEKeyword        keyword;
  508.     DescType        returnedType;
  509.     FSSpec            **droppings = NULL;        // FSSpecs of Finder objects dragged-n-dropped
  510.     
  511.     err = AEGetParamDesc (theAppleEvent, keyDirectObject, typeAEList, &docList);
  512.     if (err)
  513.         return err;
  514.  
  515.     // OK, docList contains a valid allocated AEDescList ╤ now get file specs from it
  516.     
  517.     // First, make sure nothing's there that we didn't expect ╤ this is a formality for 'odoc' events
  518.     err = GotRequiredParams (theAppleEvent);
  519.     if (err == noErr) {
  520.     
  521.         // How many files/folders are there?
  522.         err = AECountItems (&docList, &numDocs);
  523.         
  524.         // I don't think numDocs can be <= 0, but ╔
  525.         if (err == noErr && numDocs > 0) {
  526.             // Allocate a relocatable block ╤ use temporary memory if necessary
  527.             droppings = (FSSpec **) AnyHandle (numDocs * sizeof (FSSpec));
  528.             if (droppings != NULL) {
  529.                 HLock ((Handle) droppings);
  530.                 for (i = 1, fss = *droppings; i <= numDocs; i++, fss++) {
  531.                     err = AEGetNthPtr (&docList, i, typeFSS, &keyword,
  532.                             &returnedType, (Ptr) fss, sizeof (FSSpec), &actualSize);
  533.                     if (err) break;
  534.                 }
  535.                 HUnlock ((Handle) droppings);
  536.                 
  537.                             // => This is where the whole shebang gets called!
  538.                 
  539.                 err = ProcessDroppings (droppings, numDocs);
  540.                 
  541.                             // NOTE:    ProcessDroppings disposes of droppings
  542.                             //     (can you say "draggie bag"?)
  543.             }
  544.         }
  545.     }
  546.     
  547.     // All done ╤ clean up (we don't get here if docList wasn't successfully extracted)
  548.     (void) AEDisposeDesc (&docList);
  549.     
  550.     if (autoQuit)                // Should we quit immediately after handling this event?
  551.         StopRunning ();
  552.     else if (!menusInstalled)        // We'll set up our menus here if (and only if) they haven't
  553.         SetUpMenus ();        //     already been installed
  554.     return err;
  555. }
  556.  
  557. OSErr GotRequiredParams (AppleEvent *theEvent)
  558. {
  559.     DescType    returnedType;
  560.     long            actualSize;
  561.     OSErr        err;
  562.     
  563.     err = AEGetAttributePtr (theEvent, keyMissedKeywordAttr, typeWildCard, &returnedType, NULL, 0, &actualSize);
  564.     if (err == errAEDescNotFound)
  565.         return noErr;
  566.     else if (err == noErr)
  567.         return errAEEventNotHandled;
  568.     else
  569.         return err;
  570. }
  571.  
  572. void Dragon::Abort (short errNum)
  573. {
  574.     Error (errNum);
  575.     Finish ();
  576.     ExitToShell ();        // Sloppy, but it works
  577. }
  578.  
  579. void Dragon::Error (short errNum)
  580. {
  581.     Str255    string;
  582.     
  583.     NumToString (errNum, &string);
  584.     ParamText (&string, "\p", "\p", "\p");
  585.     Alert (rErrorAlert, NULL);
  586. }